package cn.xiaoyaoji.plugin.html;

import java.io.File;
import java.io.StringReader;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;


public class XmlConvertor
{
  private static final String[] CONTAINERS = { "List", "Map" };
  private int groupIndex;
  private int index;
  private String name;
  private String title;
  private String method;
  private String url;
  private String desc;
  private String invoker;
  private String lastmodifyinfo;
  private String example;
  Element tableElement;
  Element cacheElement;
  Element interfaceElement;
  private boolean ready;
  Element requestElement;
  Element responseElement;
  Element resultCodesElement;
  File xmlFile;
  static String argsDsfMethodName;
  static String argsIsNeedDB;
  
  public XmlConvertor(int groupIndex, int index)
  {
    this.groupIndex = groupIndex;
    this.index = index;
    this.ready = false;
  }
  
  public boolean convert(String xmlFile)
  {
    this.ready = false;
    try
    {
      StringReader sr = new StringReader(xmlFile); 
	  InputSource is = new InputSource(sr); 
      DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
      DocumentBuilder builder = builderFactory.newDocumentBuilder();
      Document doc = builder.parse(is);
     
      Element root = doc.getDocumentElement();
      this.title ="";
      
      NodeList childList = root.getChildNodes();
      int childCnt = childList.getLength();
      for (int i = 0; i < childCnt; i++)
      {
        Node childNode = childList.item(i);
        if (childNode.getNodeType() == 1)
        {
          Element element = (Element)childNode;
          String tagName = element.getTagName();
          if ("url".equals(tagName)) {
            this.url = element.getTextContent();
            String[] names = element.getTextContent().split("/");
            this.name = names[names.length-1];
          } else if ("description".equals(tagName)) {
            this.desc = element.getTextContent();
          } else if ("requestArgs".equals(tagName)) {
        	  this.requestElement = element;
          } else if ("responseArgs".equals(tagName)) {
            this.responseElement = element;
          } else if ("invoker".equals(tagName)) {
            this.invoker = element.getTextContent();
          } else if ("lastmodifyinfo".equals(tagName)) {
            this.lastmodifyinfo = element.getTextContent();
          } else if ("tables".equals(tagName)) {
            this.tableElement = element;
          } else if ("caches".equals(tagName)) {
            this.cacheElement = element;
          } else if ("interfaces".equals(tagName)) {
            this.interfaceElement = element;
          } else if ("ResultCodes".equals(tagName)) {
            this.resultCodesElement = element;
          } else if ("requestMethod".equals(tagName)) {
              this.method = element.getTextContent();
          }  else if ("example".equals(tagName)) {
              this.example = element.getTextContent();
          }
        }
      }
      this.ready = true;
    }
    catch (Exception e)
    {
      System.out.println("Parse error, xml path: ");
      e.printStackTrace();
    }
    if ((this.name == null) || ("".equals(this.name))) {
      this.ready = false;
    }
    return this.ready;
  }
  
  public String toHtmlString()
  {
    if (this.ready)
    {
      String itemTemplate = HtmlTemplates.getInstance().getItemTemplate();
      if (this.method.equalsIgnoreCase(argsDsfMethodName)) {
        itemTemplate = HtmlTemplates.getInstance().getItemTemplateForDSF();
      }
      if ("0".equals(argsIsNeedDB)) {
        itemTemplate = HtmlTemplates.getInstance().getItemTemplateForNoDB();
      }
      itemTemplate = itemTemplate.replace("${groupindex}", String.valueOf(this.groupIndex + 1));
      itemTemplate = itemTemplate.replace("${index}", String.valueOf(this.index + 1));
      itemTemplate = itemTemplate.replace("${name}", this.name);
      itemTemplate = itemTemplate.replace("${title}", this.title);
      itemTemplate = itemTemplate.replace("${desc}", this.desc);
      this.invoker = (this.invoker == null ? "" : this.invoker);
      itemTemplate = itemTemplate.replace("${invoker}", this.invoker);
      this.lastmodifyinfo = (this.lastmodifyinfo == null ? "" : this.lastmodifyinfo);
      itemTemplate = itemTemplate.replace("${lastmodifyinfo}", this.lastmodifyinfo);
      
      String requestRows = buildTableRows(this.requestElement);
      String responseRows = buildTableRows(this.responseElement);
      String tableRows = buildTableRowsForDateSource(this.tableElement, 1);
      String cacheRows = buildTableRowsForDateSource(this.cacheElement, 2);
      String interfaceRows = buildTableRowsForDateSource(this.interfaceElement, 3);
      
      itemTemplate = itemTemplate.replace("${request_rows}", requestRows);
      itemTemplate = itemTemplate.replace("${response_rows}", responseRows);
      itemTemplate = itemTemplate.replace("${table_rows}", tableRows);
      itemTemplate = itemTemplate.replace("${cache_rows}", cacheRows);
      itemTemplate = itemTemplate.replace("${interface_rows}", interfaceRows);
      
      itemTemplate = itemTemplate.replace("${method}", this.method);
      itemTemplate = itemTemplate.replace("${url}", this.url);
      
      String requestExampleString =  this.example;
      if (null!= requestExampleString) 
      {
    	  requestExampleString = requestExampleString.replace("{", "{\n\t");
    	  requestExampleString = requestExampleString.replace(",", ",\n\t");
    	  requestExampleString = requestExampleString.replace("}", "\n\t}");
    	  requestExampleString = requestExampleString.replace("响应", "\n响应");
	  }
      else 
	  {
		  requestExampleString = "";
	  }
      //String responseExampleString = buildExampleString(this.responseElement);
      itemTemplate = itemTemplate.replace("${request_example}", requestExampleString);
      //itemTemplate = itemTemplate.replace("${response_example}", response_example);
      String resultCodesString = buildResultCodeRows(this.resultCodesElement);
      itemTemplate = itemTemplate.replace("${result_codes}", resultCodesString);
      return itemTemplate;
    }
    return "";
  }
  
  private String buildTableRows(Element element)
  {
    if (element == null) {
      return "";
    }
    StringBuilder sBuilder = new StringBuilder();
    String parentName = element.getTagName();
    //String parentName = null;
    NodeList childs = element.getChildNodes();
    int nodeSize = childs.getLength();
    for (int i = 0; i < nodeSize; i++)
    {
      Node childnode = childs.item(i);
      
      if (childnode.getNodeType() == 1) {
    	 
            dealTableChildElement((Element) childnode, sBuilder, parentName, 0);
    	 
      }
      
    }
    return sBuilder.toString();
  }
  
  private String buildTableRowsForDateSource(Element element, int templateType)
  {
    if (element == null) {
      return "";
    }
    StringBuilder sBuilder = new StringBuilder();
    String parentName = element.getTagName();
    NodeList childs = element.getChildNodes();
    int nodeSize = childs.getLength();
    for (int i = 0; i < nodeSize; i++)
    {
      Node childnode = childs.item(i);
      if (childnode.getNodeType() == 1) {
    	 
    		  dealTableChildElementForTableAndCache((Element)childnode, sBuilder, parentName, 0, templateType);
		
      }
    }
    return sBuilder.toString();
  }
  
  private String buildResultCodeRows(Element element)
  {
    if (element == null) {
      return "";
    }
    NodeList resultCodeElements = element.getElementsByTagName("resultcode");
    int resultCodeCount = resultCodeElements.getLength();
    
    StringBuilder sBuilder = new StringBuilder(100);
    for (int i = 0; i < resultCodeCount; i++)
    {
      Element resultCodeElement = (Element)resultCodeElements.item(i);
      String resultCode = resultCodeElement.getAttribute("value");
      String desc = resultCodeElement.getAttribute("desc");
      String rowTemplate = HtmlTemplates.getInstance().getResultCodeRowTemplate();
      rowTemplate = rowTemplate.replace("${resultcode}", resultCode);
      rowTemplate = rowTemplate.replace("${desc}", desc);
      sBuilder.append(rowTemplate);
    }
    return sBuilder.toString();
  }
  
  private void dealTableChildElement(Element element, StringBuilder sBuilder, String parentName, int parentLevel)
  {
    int currLevel = parentLevel + 1;
    
    String tagName = "默认";
    String desc = "默认";
    String type = "string";
    String occurrence = "0...1";
    
    NodeList childss = element.getChildNodes();
    int nodeSizes = childss.getLength();
    for (int i = 0; i < nodeSizes; i++)
    {
      Node childnode = childss.item(i);
      if (childnode.getNodeType() == 1) {
    	  Element elements = (Element)childnode;
    	  String tagNames = elements.getTagName();
          if ("name".equals(tagNames)) {
        	  tagName = elements.getTextContent();
          } else if ("description".equals(tagNames)) {
        	  desc = elements.getTextContent();
          }else if ("type".equals(tagNames)) {
        	  type = elements.getTextContent();
          }else if ("require".equals(tagNames)) {
        	  occurrence = elements.getTextContent();
        	 
          }
      }
    }
    
    String tableRowTemplate = HtmlTemplates.getInstance().getTableRowTemplate();
    tableRowTemplate = tableRowTemplate.replace("${level}", String.valueOf(currLevel));
    tableRowTemplate = tableRowTemplate.replace("${name}", tagName);
    tableRowTemplate = tableRowTemplate.replace("${parent}", parentName);
    tableRowTemplate = tableRowTemplate.replace("${type}", type);
    tableRowTemplate = tableRowTemplate.replace("${occurrence}", occurrence);
    tableRowTemplate = tableRowTemplate.replace("${desc}", desc);
    sBuilder.append(tableRowTemplate);
   
    NodeList childs = element.getChildNodes();
    int nodeSize = childs.getLength();
    for (int i = 0; i < nodeSize; i++)
    {
      Node childnode = childs.item(i);
      if (childnode.getNodeType() == 1)
      {
    	  Element element2 = (Element)childnode;
    	  String name = element2.getTagName();
    	  if ("children".equals(name)) {
    		  NodeList chi = element2.getChildNodes();
    		  int nodeSi = chi.getLength();
    		  for (int j = 0; j < nodeSi; j++)
    		    {
    		      Node childno = chi.item(j);
    		      if (childno.getNodeType() == 1)
    		      {
    		    	  
    		    	  dealTableChildElement((Element)childno, sBuilder, tagName, currLevel);
    		      }
    		      
    		    }  
		 }
      }
    }
  }
  
  private void dealTableChildElementForTableAndCache(Element element, StringBuilder sBuilder, String parentName, int parentLevel, int templateType)
  {
    int currLevel = parentLevel + 1;
    String tagName = element.getNodeName();
    String name = element.getAttribute("name");
    String desc = element.getAttribute("desc");
    String type = element.getAttribute("type");
    String occurrence = element.getAttribute("occurrence");
    String endpointname = element.getAttribute("endpointname");
    String servicename = element.getAttribute("servicename");
    String interfacename = element.getAttribute("interfacename");
    
    String rowTemplate = "";
    if (templateType == 1) {
      rowTemplate = HtmlTemplates.getInstance().getDataSourceRowTemplate();
    }
    if (templateType == 2) {
      rowTemplate = HtmlTemplates.getInstance().getCacheRowTemplate();
    }
    if (templateType == 3) {
      rowTemplate = HtmlTemplates.getInstance().getInterfaceServiceRowTemplate();
    }
    rowTemplate = rowTemplate.replace("${level}", String.valueOf(currLevel));
    rowTemplate = rowTemplate.replace("${name}", name);
    rowTemplate = rowTemplate.replace("${parent}", parentName);
    rowTemplate = rowTemplate.replace("${type}", type);
    rowTemplate = rowTemplate.replace("${occurrence}", occurrence);
    rowTemplate = rowTemplate.replace("${desc}", desc);
    rowTemplate = rowTemplate.replace("${endpointname}", endpointname);
    rowTemplate = rowTemplate.replace("${servicename}", servicename);
    rowTemplate = rowTemplate.replace("${interfacename}", interfacename);
    sBuilder.append(rowTemplate);
    
    NodeList childs = element.getChildNodes();
    int nodeSize = childs.getLength();
    for (int i = 0; i < nodeSize; i++)
    {
      Node childnode = childs.item(i);
      if (childnode.getNodeType() == 1)
      {
        dealTableChildElement((Element)childnode, sBuilder, tagName, currLevel);
        if (isElementContainer(element)) {
          break;
        }
      }
    }
  }
  
  private String buildExampleString(Element element)
  {
    if (element == null) {
      return "";
    }
    String useStream = element.getAttribute("userstream");
    if ("true".equals(useStream)) {
      return buildOutputStreamString(element);
    }
    return elementToEscapeStr(element, true);
  }
  
  private String buildOutputStreamString(Element element)
  {
    String zipMethod = element.getAttribute("zipMethod");
    if (zipMethod == null) {
      zipMethod = "";
    }
    String sep = element.getAttribute("sep");
    if ((sep == null) || (sep.isEmpty())) {
      sep = "|";
    }
    StringBuffer sBuffer = new StringBuffer();
    if (!zipMethod.isEmpty()) {
      sBuffer.append(zipMethod).append("(\n");
    }
    LineWriter writer = new LineWriter();
    dealStreamElement(element, writer, sep);
    sBuffer.append(writer.toString());
    if (!zipMethod.isEmpty()) {
      sBuffer.append(")");
    }
    return sBuffer.toString();
  }
  
  private static boolean isTextElement(Element element)
  {
    NodeList childs = element.getChildNodes();
    int nodeSize = childs.getLength();
    for (int i = 0; i < nodeSize; i++)
    {
      Node childnode = childs.item(i);
      if (childnode.getNodeType() == 1) {
        return false;
      }
    }
    return true;
  }
  
  private static boolean isTextParentElement(Element element)
  {
    NodeList childs = element.getChildNodes();
    int nodeSize = childs.getLength();
    for (int i = 0; i < nodeSize; i++)
    {
      Node childnode = childs.item(i);
      if (childnode.getNodeType() == 1) {
        if (isTextElement((Element)childnode)) {
          return true;
        }
      }
    }
    return false;
  }
  
  private static void dealStreamElement(Element element, LineWriter writer, String sep)
  {
    try
    {
      NodeList childs = element.getChildNodes();
      int nodeSize = childs.getLength();
      
      boolean isTextElement = isTextElement(element);
      boolean isTextParentElement = isTextParentElement(element);
      if (isTextParentElement) {
        writer.intoTextParent();
      }
      for (int i = 0; i < nodeSize; i++)
      {
        Node childnode = childs.item(i);
        if (childnode.getNodeType() == 3)
        {
          if (isTextElement)
          {
            String text = childnode.getNodeValue().trim();
            if (writer.isFirstChildText())
            {
              writer.appendText(text);
              writer.leaveFirstChildText();
            }
            else
            {
              writer.appendText(sep);
              writer.appendText(text);
            }
          }
        }
        else if (childnode.getNodeType() == 1) {
          dealStreamElement((Element)childnode, writer, sep);
        }
      }
      if (isTextParentElement) {
        writer.newLine();
      }
    }
    catch (Exception e)
    {
      e.printStackTrace();
    }
  }
  
  private static void dealElement(Element element, XmlWriter writer, boolean trimAttribute)
  {
    try
    {
    	
      writer.beginStartTag(element.getNodeName());
      if (!trimAttribute)
      {
        NamedNodeMap map = element.getAttributes();
        int attrSize = map.getLength();
        for (int i = 0; i < attrSize; i++)
        {
          Node Attrnode = map.item(i);
          String name = Attrnode.getNodeName();
          String value = Attrnode.getNodeValue();
          writer.writeAttribute(name, value);
        }
      }
      writer.endTag();
      
      NodeList childs = element.getChildNodes();
      int nodeSize = childs.getLength();
      boolean intoChild = false;
      for (int i = 0; i < nodeSize; i++)
      {
        Node childnode = childs.item(i);
        if (childnode.getNodeType() == 1)
        {
          intoChild = true;
          break;
        }
      }
      if (intoChild) {
        writer.intoChild();
      }
      for (int i = 0; i < nodeSize; i++)
      {
        Node childnode = childs.item(i);
        if (childnode.getNodeType() == 3)
        {
        	  String text = childnode.getNodeValue().trim();
        	  if (!text.isEmpty()) 
        	  {
        		  writer.text(childnode.getNodeValue());
        	  }
        }
        else if (childnode.getNodeType() == 1)
        {
        	  dealElement((Element)childnode, writer, trimAttribute);
        }
      }
      if (intoChild) {
        writer.outOfChild();
      }
      writer.endStartTag(element.getNodeName());
    }
    catch (Exception e)
    {
      e.printStackTrace();
    }
  }
  
  public String toDirHtmlString()
  {
    if (!this.ready) {
      return "";
    }
    String templateString = HtmlTemplates.getInstance().getDirItemTemplate();
    templateString = templateString.replace("${name}", this.name);
    templateString = templateString.replace("${groupindex}", String.valueOf(this.groupIndex + 1));
    templateString = templateString.replace("${index}", String.valueOf(this.index + 1));
    templateString = templateString.replace("${title}", this.title);
    return templateString;
  }
  
  private boolean isElementContainer(Element element)
  {
    if (element == null) {
      return false;
    }
    String type = element.getAttribute("type");
    for (int i = 0; i < CONTAINERS.length; i++) {
      if (CONTAINERS[i].equals(type)) {
        return true;
      }
    }
    return false;
  }
  
  public String getName()
  {
    return this.name;
  }
  
  public static String elementToStr(Element elem, boolean trimAttribute)
  {
    try
    {
      XmlWriter serializer = new XmlWriter(0);
      dealElement(elem, serializer, trimAttribute);
      return serializer.toString();
    }
    catch (Exception e)
    {
      e.printStackTrace();
    }
    return "";
  }
  
  public static String elementToEscapeStr(Element elem, boolean trimAttribute)
  {
    String originalString = elementToStr(elem, trimAttribute);
    return originalString.replace("<", "&lt;").replace(">", "&gt;");
  }
  
}
